package br.com.cfccultura.admin.model.dao.mysql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import br.com.cfccultura.admin.model.dao.IFluxoCaixaDAO;
import br.com.cfccultura.admin.model.dao.IOrdemServicoDAO;
import br.com.cfccultura.admin.model.enums.SituacaoConta;
import br.com.cfccultura.admin.model.enums.TipoMovimento;
import br.com.cfccultura.admin.model.vo.ContaPagar;
import br.com.cfccultura.admin.model.vo.ContaReceber;
import br.com.cfccultura.admin.model.vo.Fornecedor;
import br.com.cfccultura.admin.model.vo.Funcionario;
import br.com.cfccultura.admin.model.vo.Movimento;
import br.com.cfccultura.admin.model.vo.MovimentoAnexo;
import br.com.cfccultura.admin.model.vo.OrdemServico;
import br.com.cfccultura.admin.model.vo.OrdemServicoEspecie;

@Repository
public class FluxoCaixaDAO extends DAO implements IFluxoCaixaDAO {
	
	@Autowired
	private IOrdemServicoDAO ordemServicoDAO;
	
	@Override
	public void incluir(final ContaReceber vo) {
		KeyHolder keyHolder = new GeneratedKeyHolder();
		update(new PreparedStatementCreator() {			
			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				String sql = "INSERT INTO tb_conta_receber(cd_ordem_servico, cd_ordem_servico_especie, id_situacao, dt_vencimento, vl_pagamento, dt_pagamento, cd_funcionario_atualizacao, dc_conta_receber) values(?,?,?,?,?,?,?,?)";
				PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

			    ps.setInt(1, vo.getOrdemServicoEspecie().getOrdemServico().getCodigo());
				ps.setInt(2, vo.getOrdemServicoEspecie().getCodigo());
				ps.setInt(3, vo.getSituacao().getValue());
				ps.setDate(4, new java.sql.Date(vo.getVencimento().getTime()));
				ps.setDouble(5, vo.getValorPagamento());
				ps.setObject(6, vo.getPagamento());
				ps.setInt(7, vo.getUsuarioSistema().getCodigo());
				ps.setString(8, vo.getDescricao());
			    return ps;
			}
		}, keyHolder);
		
		vo.setCodigo(keyHolder.getKey().intValue());
		
		Movimento movimento = new Movimento();
		movimento.setContaReceber(vo);
		movimento.setDataMovimento(new Date());
		movimento.setTipoMovimento(TipoMovimento.RECEBER);
		movimento.setUsuarioSistema(vo.getUsuarioSistema());
		
		incluir(movimento);
	}
	
	@Override
	public void incluir(final ContaPagar vo) {
		KeyHolder keyHolder = new GeneratedKeyHolder();
		update(new PreparedStatementCreator() {			
			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				String sql = "INSERT INTO tb_conta_pagar(cd_fornecedor, dc_conta_pagar, dt_vencimento, dt_cadastro, vl_pagamento) values(?,?,?,?,?)";
				PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

			    ps.setInt(1, vo.getFornecedor().getCodigo());
			    ps.setString(2, vo.getDescricao());
				ps.setDate(3, new java.sql.Date(vo.getVencimento().getTime()));
				ps.setTimestamp(4, new java.sql.Timestamp(new Date().getTime()));
				ps.setDouble(5, vo.getValor());
			    return ps;
			}
		}, keyHolder);
		
		vo.setCodigo(keyHolder.getKey().intValue());
		
		Movimento movimento = null;
		if(vo.getMovimento() == null) {
			movimento = new Movimento();
		} else {
			movimento = vo.getMovimento();
		}
		
		movimento.setContaPagar(new ContaPagar(vo.getCodigo()));
		movimento.setDataMovimento(new Date());
		movimento.setTipoMovimento(TipoMovimento.PAGAR);
		movimento.setUsuarioSistema(vo.getUsuarioSistema());
		
		incluir(movimento);
		
	}
	
	private void incluir(final Movimento vo) {
		KeyHolder keyHolder = new GeneratedKeyHolder();
		update(new PreparedStatementCreator() {			
			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				String sql = "INSERT INTO tb_movimento(id_tipo_movimento, cd_conta_receber, cd_conta_pagar, dt_movimento, cd_funcionario_cadastro) values(?,?,?,?,?)";
				PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

			    ps.setInt(1, vo.getTipoMovimento().getValue());
				ps.setObject(2, vo.getContaReceber() != null ? vo.getContaReceber().getCodigo() : null);
				ps.setObject(3, vo.getContaPagar() != null ? vo.getContaPagar().getCodigo() : null);
				ps.setObject(4, vo.getDataMovimento());
				ps.setObject(5, vo.getUsuarioSistema().getCodigo());
			    return ps;
			}
		}, keyHolder);
		vo.setCodigo(keyHolder.getKey().intValue());
		
		for(MovimentoAnexo anexo : vo.getAnexos()) {
			incluir(vo.getCodigo(), anexo);
		}
		
	}
	
	@Override
	public void incluir(final Integer codigoMovimento, final MovimentoAnexo vo) {
		KeyHolder keyHolder = new GeneratedKeyHolder();
		update(new PreparedStatementCreator() {
			@Override
			public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
				String sql = "INSERT INTO tb_movimento_anexo(cd_movimento_financeiro, nm_anexo, bn_anexo, dt_cadastro, id_tipo_anexo) values(?,?,?,?,?)";
				PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
				ps.setInt(1, codigoMovimento);
				ps.setString(2, vo.getNome());
				ps.setBytes(3, vo.getArquivo());
				ps.setTimestamp(4, new java.sql.Timestamp(System.currentTimeMillis()));
				ps.setInt(5, vo.getTipo().getValue());			 
			    return ps;
			}
		}, keyHolder);
	}
	
	@Override
	public void alterar(final ContaPagar vo) {
		String sql = "UPDATE tb_conta_pagar SET cd_fornecedor = ?, dc_conta_pagar = ?, dt_vencimento = ?, dt_cadastro = ?, vl_pagamento = ? WHERE cd_conta_pagar = ?";
		update(sql, new Object[]{vo.getFornecedor().getCodigo(), vo.getDescricao(), vo.getVencimento(), new Date(), vo.getValor(), vo.getCodigo()});
	}	
	
	public List<ContaReceber> verificaDebito(OrdemServico vo) {
		String sql = "SELECT os.cd_ordem_servico, receber.cd_conta_receber, receber.id_situacao, receber.cd_ordem_servico_especie, receber.vl_pagamento, os.vl_total " +
				"FROM tb_ordem_servico os, tb_conta_receber receber " +
				"WHERE os.cd_ordem_servico = receber.cd_ordem_servico and os.cd_ordem_servico = ?";
		List<ContaReceber> result = query(sql, new Object[] {vo.getCodigo()}, new RowMapper<ContaReceber>() {
			@Override      
			public ContaReceber mapRow(ResultSet rs, int arg1) throws SQLException {
				ContaReceber vo = new ContaReceber();
				vo.setCodigo(rs.getInt("receber.cd_conta_receber"));
				vo.setSituacao(SituacaoConta.setValue(rs.getInt("receber.id_situacao")));
				vo.setValorPagamento(rs.getDouble("receber.vl_pagamento"));
				vo.setOrdemServicoEspecie(new OrdemServicoEspecie(rs.getInt("receber.cd_ordem_servico_especie")));
				vo.getOrdemServicoEspecie().setOrdemServico(new OrdemServico(rs.getInt("os.cd_ordem_servico")));
				vo.getOrdemServicoEspecie().getOrdemServico().setTotal(rs.getDouble("os.vl_total"));
				return vo;
			}			
		});
		return result;
	}
	
	@Override
	public List<ContaReceber> consulta(ContaReceber vo) {
		List<Object> params = new ArrayList<Object>();
		String sql = "SELECT receber.cd_conta_receber, receber.cd_ordem_servico, receber.cd_ordem_servico_especie, receber.id_situacao, receber.dt_vencimento, " +
				"receber.vl_pagamento, receber.dt_pagamento, receber.cd_funcionario_atualizacao, receber.dc_conta_receber, os.dt_cadastro " +
				"FROM tb_conta_receber receber, tb_ordem_servico_especie especie, tb_ordem_servico os " +
				"WHERE especie.cd_ordem_servico_especie = receber.cd_ordem_servico_especie and os.cd_ordem_servico = receber.cd_ordem_servico ";
		
		if(vo.getCodigo() != null) {
			sql += "and receber.cd_conta_receber = ? ";
			params.add(vo.getCodigo());			
		} else {		
			if(vo.getSituacao() != null) {
				sql += "and receber.id_situacao = ? ";
				params.add(vo.getSituacao().getValue());
			}
			
			if(vo.getOrdemServicoEspecie() != null && vo.getOrdemServicoEspecie().getCodigo() != null) {
				sql += "and receber.cd_ordem_servico_especie = ? ";
				params.add(vo.getOrdemServicoEspecie().getCodigo());
			}
		}
		
		String orderBy = null;
		
		if(vo.getVencimento() != null) {
			sql += "and receber.dt_vencimento = DATE(?) ";
			params.add(vo.getVencimento());
			orderBy = "receber.dt_vencimento, receber.vl_pagamento";
		} else if(vo.getDataMenor() != null) {
			sql += "and receber.dt_vencimento < DATE(?) ";
			params.add(vo.getDataMenor());
			orderBy = "receber.dt_vencimento, receber.vl_pagamento";
		} else 	if(vo.getDataMaior() != null) {
			sql += "and receber.dt_vencimento > DATE(?) ";
			params.add(vo.getDataMaior());
			orderBy = "receber.dt_vencimento, receber.vl_pagamento";
		} else if(vo.getPagamento() != null) {
			sql += "and receber.dt_pagamento = DATE(?) ";
			params.add(vo.getPagamento());
			orderBy = "receber.dt_pagamento, receber.vl_pagamento";
		}
		
		if(vo.getOrdemServicoEspecie() != null && vo.getOrdemServicoEspecie().getOrdemServico() != null &&
			vo.getOrdemServicoEspecie().getOrdemServico().getCadastro() != null) {
			sql += "and DATE(os.dt_cadastro) = DATE(?) ";
			params.add(vo.getOrdemServicoEspecie().getOrdemServico().getCadastro());
			orderBy = "os.dt_cadastro, receber.vl_pagamento";
		}
		
		if(orderBy != null)
			sql += "ORDER BY " + orderBy;
		
		List<ContaReceber> result = query(sql, params.toArray(), new RowMapper<ContaReceber>() {
			@Override      
			public ContaReceber mapRow(ResultSet rs, int arg1) throws SQLException {
				ContaReceber vo = new ContaReceber();
				vo.setCodigo(rs.getInt("receber.cd_conta_receber"));
				vo.setOrdemServicoEspecie(getOrdemServicoDAO().consultarEspecie(rs.getInt("receber.cd_ordem_servico_especie")));
				vo.getOrdemServicoEspecie().getOrdemServico().setCadastro(rs.getTimestamp("os.dt_cadastro"));
				vo.setSituacao(SituacaoConta.setValue(rs.getInt("receber.id_situacao")));
				vo.setVencimento(rs.getDate("receber.dt_vencimento"));
				vo.setValorPagamento(rs.getDouble("receber.vl_pagamento"));
				if(rs.getObject("receber.dt_pagamento") != null)
					vo.setPagamento(rs.getTimestamp("receber.dt_pagamento"));
				vo.setFuncionario(new Funcionario(rs.getInt("receber.cd_funcionario_atualizacao")));
				vo.setDescricao(rs.getString("receber.dc_conta_receber"));

				return vo;
			}			
		});
		return result;
	}
	
	@Override
	public void confirmarRecebimento(ContaReceber vo, Date data) {
		String sql = "UPDATE tb_conta_receber set id_situacao = ?, dt_pagamento = ?, cd_funcionario_atualizacao = ? where cd_conta_receber = ?";
		update(sql, new Object[] {SituacaoConta.PAGO.getValue(), data, vo.getUsuarioSistema().getCodigo(), vo.getCodigo()});
	}
	
	@Override
	public void excluir(ContaReceber vo) {
		
		String sql = "DELETE FROM tb_movimento where cd_conta_receber = ?";
		update(sql, new Object[] {vo.getCodigo()});
		
		sql = "DELETE FROM tb_conta_receber WHERE cd_conta_receber = ?";
		update(sql, new Object[] {vo.getCodigo()});
	}
	
	@Override
	public List<ContaPagar> consulta(ContaPagar vo) {
		List<Object> params = new ArrayList<Object>();
		String sql = "SELECT  pagar.cd_conta_pagar, pagar.dc_conta_pagar, fornecedor.cd_fornecedor, fornecedor.dc_nome_fantasia, pagar.dt_vencimento, pagar.dt_cadastro, " +
				"pagar.vl_pagamento, pagar.dt_pagamento, pagar.vl_pago " +
				"FROM tb_conta_pagar pagar, tb_fornecedor fornecedor " +
				"WHERE pagar.cd_fornecedor = fornecedor.cd_fornecedor ";
		
		if(vo.getCodigo() != null) {
			sql += "and pagar.cd_conta_pagar = ? ";
			params.add(vo.getCodigo());			
		}
		
		String orderBy = null;
		
		if(vo.getVencimento() != null) {
			sql += "and pagar.dt_vencimento = DATE(?) ";
			params.add(vo.getVencimento());
			orderBy = "pagar.dt_vencimento, pagar.vl_pagamento";
		} else if(vo.getDataMenor() != null) {
			sql += "and pagar.dt_vencimento < DATE(?) ";
			params.add(vo.getDataMenor());
			orderBy = "pagar.dt_vencimento, pagar.vl_pagamento";
		} else 	if(vo.getDataMaior() != null) {
			sql += "and pagar.dt_vencimento > DATE(?) ";
			params.add(vo.getDataMaior());
			orderBy = "pagar.dt_vencimento, pagar.vl_pagamento";
		} else if(vo.getPagamento() != null) {
			sql += "and pagar.dt_pagamento = DATE(?) ";
			params.add(vo.getPagamento());
			orderBy = "pagar.dt_pagamento, pagar.vl_pagamento";
		}
		
		if(orderBy != null)
			sql += "ORDER BY " + orderBy;
		
		List<ContaPagar> result = query(sql, params.toArray(), new RowMapper<ContaPagar>() {
			@Override      
			public ContaPagar mapRow(ResultSet rs, int arg1) throws SQLException {
				ContaPagar vo = new ContaPagar();
				vo.setCodigo(rs.getInt("pagar.cd_conta_pagar"));
				vo.setDescricao(rs.getString("pagar.dc_conta_pagar"));
				vo.setFornecedor(new Fornecedor(rs.getInt("fornecedor.cd_fornecedor")));
				vo.getFornecedor().setNomeFantasia(rs.getString("fornecedor.dc_nome_fantasia"));
				vo.setVencimento(rs.getDate("pagar.dt_vencimento"));
				vo.setCadastro(rs.getTimestamp("pagar.dt_cadastro"));
				vo.setValor(rs.getDouble("pagar.vl_pagamento"));
				if(rs.getObject("pagar.dt_pagamento") != null)
					vo.setPagamento(rs.getTimestamp("pagar.dt_pagamento"));
				if(rs.getObject("pagar.vl_pago") != null)
					vo.setValorPago(rs.getDouble("pagar.vl_pago"));				

				return vo;
			}			
		});
		return result;
	}
	
	@Override
	public void excluir(ContaPagar vo) {
		
		String sql = "DELETE FROM tb_movimento where cd_conta_pagar = ?";
		update(sql, new Object[] {vo.getCodigo()});
		
		sql = "DELETE FROM tb_conta_pagar WHERE cd_conta_pagar = ?";
		update(sql, new Object[] {vo.getCodigo()});		
	}
	
	@Override
	public void confirmarPagamento(ContaPagar vo) {
		String sql = "UPDATE tb_conta_pagar set dt_pagamento = ?, vl_pago = ? where cd_conta_pagar = ?";
		update(sql, new Object[] {vo.getPagamento(), vo.getValorPago(), vo.getCodigo()});
	}
	
	@Override
	public List<MovimentoAnexo> consultaAnexos(Integer codigo) {
		String sql = "SELECT anexo.cd_movimento_anexo, anexo.nm_anexo, anexo.dt_cadastro ";
		sql += "FROM tb_movimento_anexo anexo, tb_movimento mov ";
		sql += "WHERE anexo.cd_movimento_financeiro = mov.cd_movimento_financeiro and (mov.cd_conta_pagar = ? or mov.cd_conta_receber = ?) ";
		sql += "ORDER BY anexo.dt_cadastro";
		
		return query(sql, new Object[] {codigo, codigo}, new RowMapper<MovimentoAnexo>() {
			@Override      
			public MovimentoAnexo mapRow(ResultSet rs, int arg1) throws SQLException {
				MovimentoAnexo vo = new MovimentoAnexo();
				vo.setCodigo(rs.getInt("anexo.cd_movimento_anexo"));
				vo.setNome(rs.getString("anexo.nm_anexo"));
				vo.setCadastro(rs.getTimestamp("anexo.dt_cadastro"));				
				return vo;
			}
		});
	}
	
	@Override
	public MovimentoAnexo consultaAnexo(Integer codigo) {		
		String sql = "SELECT bn_anexo, nm_anexo FROM tb_movimento_anexo WHERE cd_movimento_anexo = ?";		
		return queryForObject(sql, new Object[] {codigo}, new RowMapper<MovimentoAnexo>() {
			@Override      
			public MovimentoAnexo mapRow(ResultSet rs, int arg1) throws SQLException {
				MovimentoAnexo vo = new MovimentoAnexo();
				vo.setArquivo(rs.getBytes("bn_anexo"));
				vo.setNome(rs.getString("nm_anexo"));
				return vo;
			}			
		});
	}
	
	@Override
	public Movimento consultaMovimento(Integer codigo) {
		String sql = "SELECT cd_movimento_financeiro, id_tipo_movimento, dt_movimento ";
		sql += "FROM tb_movimento ";
		sql += "WHERE cd_conta_pagar = ? or cd_conta_receber = ? ";
		
		return queryForObject(sql, new Object[] {codigo, codigo}, new RowMapper<Movimento>() {
			@Override      
			public Movimento mapRow(ResultSet rs, int arg1) throws SQLException {
				Movimento vo = new Movimento();
				vo.setCodigo(rs.getInt("cd_movimento_financeiro"));
				vo.setTipoMovimento(TipoMovimento.setValue(rs.getInt("id_tipo_movimento")));
				vo.setDataMovimento(rs.getTimestamp("dt_movimento"));				
				return vo;
			}
		});
	}
	
	public IOrdemServicoDAO getOrdemServicoDAO() {
		return ordemServicoDAO;
	}
	public void setOrdemServicoDAO(IOrdemServicoDAO ordemServicoDAO) {
		this.ordemServicoDAO = ordemServicoDAO;
	}

}
